package com.example.websocket.redis.util;

import com.example.websocket.excel.util.StringUtils;
import net.sf.cglib.beans.BeanMap;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 定义javaBean相关操作方法
 * @author Gaojt
 *
 */
public class BeanUtils {
	/**
	 * 对两个对象里的Number和BigDecimal属性，进行计算
	 * @param <T>
	 * @param source
	 * @param target
	 * @param isAdd true为加，false为减
	 * @return
	 */
	@SuppressWarnings("unchecked")
	private static<T> T _count(T first, T second, boolean isAdd) {
		T result = copy(first);
		BeanMap firstMap = BeanMap.create(result);
		BeanMap secondMap = BeanMap.create(second);
		firstMap.forEach((k, firstValue) -> {
			Object secondValue = secondMap.get(k);
			if (firstValue == null && secondValue == null) {
				return;
			}
			Object _v = Optional.ofNullable(firstValue).orElse(secondValue);
			if (_v instanceof Number || _v instanceof BigDecimal) {// 是计算属性
				firstMap.put(k, isAdd 
						? DataUtils.sums(firstValue, secondValue)
						: DataUtils.subtracts(firstValue, secondValue));
			};
		});
		return result;
	}
	/**
	 * 对两个对象里的Number和BigDecimal属性，进行相加
	 * @param <T>
	 * @param first
	 * @param second
	 * @return
	 */
	public static<T> T add(T first, T second) {
		return _count(first, second, true);
	}
	/**
	 * 对两个对象里的Number和BigDecimal属性，进行相减
	 * @param <T>
	 * @param first
	 * @param second
	 * @return
	 */
	public static<T> T subtract(T first, T second) {
		return _count(first, second, false);
	}
	/**
	 * 复制对象，原始对象为null的属性，不进行复制
	 * @param source
	 * @param target
	 */
	public static void copyNonNull(Object source, Object target, String... notCopyProperties) {
		copy(source, target, null, false, notCopyProperties);
	}
	/**
	 * 将对象中String类型属性，值为空值的设置为null
	 * @param t
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static<T> T blankToNull(T t) {
		if (t == null)
			return t;
		BeanMap sourceMap = BeanMap.create(t);
		sourceMap.forEach((k, v) -> {
			if(v == null)
				return;
			else if(v instanceof String) {
				if(((String) v).trim().length() == 0){
					sourceMap.put(k, null);
				}
			}
		});
		return t;
	}
	/**
	 * 创建空对象
	 * @param c
	 * @return
	 */
	public static<T> T empty(Class<T> c){
		if(c == null)
			return null;
		try {
			if(c.isInterface())//接口，不允许复制
				return null;
			if(!ClassUtils.hasConstructor(c))//没有默认构造函数，不允许复制
				return null;
			T t = c.newInstance();
			return t;
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			e1.printStackTrace();
		}
		return null;
	}
	/**
	 * 新建类型c的对象，复制指定类型数据
	 * @param e 原数据对象
	 * @param c 数据类型
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	public static<T, E> T copy(E e, Supplier<T> c, String... notCopyProperties){
		if(e == null || c == null)
			return null;
		T t = c.get();
		copy(e, t, notCopyProperties);
		return t;
	}
	/**
	 * 新建类型c的对象，复制指定类型数据
	 * @param e 原数据对象
	 * @param c 数据类型
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	public static<T, E> T copy(E e, Class<T> c, String... notCopyProperties){
		if(e == null)
			return null;
		try {
			if(c.isInterface())//接口，不允许复制
				return null;
			if(!ClassUtils.hasConstructor(c))//没有默认构造函数，不允许复制
				return null;
			T t = c.newInstance();
			copy(e, t, notCopyProperties);
			return t;
		} catch (InstantiationException e1) {
			e1.printStackTrace();
		} catch (IllegalAccessException e1) {
			e1.printStackTrace();
		}
		return null;
	}
	/**
	 * 复制集合内指定类型数据
	 * @param list
	 * @param c 数据类型
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	public static<T, E> List<T> copy(List<E> list, Class<T> c, String... notCopyProperties){
		if(list == null)
			return null;
		List<T> tlist = new ArrayList<>();
		for (E e : list) {
			T t = copy(e, c, notCopyProperties);
			tlist.add(t);
		}
		return tlist;
	}
	/**
	 * List类型转换
	 * @param po
	 * @return
	 */
	public static <V, P> List<V> copy(List<P> poList, Function<P, V> fun) {
		return Optional.ofNullable(poList)
				.map(l -> l.stream().map(fun).collect(Collectors.toList()))
				.orElse(null);
	}
	/**
	 * 复制集合内指定类型数据
	 * @param list
	 * @param c 数据类型
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	public static<T, E> Set<T> copy(Set<E> list, Class<T> c, String... notCopyProperties){
		if(list == null)
			return null;
		Set<T> tlist = new HashSet<>();
		for (E e : list) {
			T t = copy(e, c, notCopyProperties);
			tlist.add(t);
		}
		return tlist;
	}
	/**
	 * 复制数组数据
	 * @param array
	 * @param c 数据类型
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static<T, E> E[] copy(T[] array, Class<E> c, String... notCopyProperties){
		if(array == null)
			return null;
		List<E> tlist = new ArrayList<>();
		for (int i = 0; i < array.length; i++) {
			tlist.add(copy(array[i], c, notCopyProperties));
		}
		return (E[])tlist.toArray();
	}
	/**
	 * 复制JAVABEAN，用于将bean从Hibernate持久化Bean中分离出来，使得取Bean属性时不需查询数据库
	 * 操作时，复制所有非Collection对象的属性
	 * @param bean
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static<T> T copy(T bean, String... notCopyProperties) {
		if(bean==null)
			return null;
		Class<T> beanClass = (Class<T>)bean.getClass();
		//是否是代理类，兼容Hibernate
		if(beanClass.isArray()){//数组
			Object[] obj = (Object[])bean;
			Class<?> c = bean.getClass().getComponentType();
			T t = (T)copy(obj, c, notCopyProperties);
			return t;
		}
//		if (isProxy(beanClass)){//代理类，还原成本类
//			beanClass = (Class<T>)beanClass.getSuperclass();
//		}
		if(ClassUtils.isBaseClass(beanClass))//基本数据类型
			return bean;
		//判断集合
		if(bean instanceof List) {
			List<?> l = (List<?>)bean;
			if(l.size() == 0) {
				return (T)new ArrayList<>();
			}else {
				Class<?> ctype = l.iterator().next().getClass();
				return (T)copy(l, ctype, notCopyProperties);
			}
		}
		if(bean instanceof Set) {
			Set<?> l = (Set<?>)bean;
			if(l.size() == 0) {
				return (T)new HashSet<>();
			}else {
				Class<?> ctype = l.iterator().next().getClass();
				return (T)copy(l, ctype, notCopyProperties);
			}
		}
		if(bean instanceof Map) {
			Map<?, ?> l = (Map<?, ?>)bean;
			if(l.size() == 0) {
				return (T)new HashMap<>();
			}else {
				Map<Object, Object> map = new HashMap<>();
				l.forEach(map::put);
				return (T)map;
			}
		}
		return copy(bean, beanClass, notCopyProperties);
	}
	/**
	 * 复制指定属性数据
	 * @param source
	 * @param target
	 * @param copyProperties
	 */
	public static void copyByProperties(Object source, Object target, String... copyProperties) {
		List<String> properties = getBeanAttribute(target.getClass());
		properties.removeAll(Arrays.asList(copyProperties));
		copy(source, target, properties.toArray(new String[0]));
	}
	/**
	 * 复制JAVABEAN，用于将bean从Hibernate持久化Bean中分离出来，使得取Bean属性时不需查询数据库
	 * 
	 * @param bean
	 * @param notCopyProperties 不进行复制的属性名称，名称命名规范遵循JavaBean
	 * @return
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	public static void copy(Object source, Object target, String... notCopyProperties ){
		copy(source, target, null, null, notCopyProperties);
	}
	/**
	 * 复制对象属性
	 * @param source 复制的原始对象
	 * @param target 复制的目标对象
	 * @param applyFun 对属性的数据类型，进行分别的处理
	 * @param nullCopy 是否复制为null的属性，默认复制（null）
	 * @param notCopyProperties 指定属性进行复制
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void copy(Object source, Object target, 
			BiFunction<Object, Class<?>, Object> applyFun, 
			Boolean nullCopy, String... notCopyProperties) {
		if(source==null || target == null)
			return;
		if(source.getClass().isArray() ^ target.getClass().isArray())//必须同时是数组或者同时不是数字
			return;
		if(source.getClass().isArray()) {//是数组，则复制数组
			Class<?> ct = target.getClass().getComponentType();
			Object[] as = (Object[])source;
			target = copy(as, ct, notCopyProperties);
			return;
		} 
		if(source instanceof Collection) {//集合复制
			Collection<?> l = (Collection<?>)source;
			if(target instanceof Collection) {
				Collection c = (Collection)target;
				c.clear();
				c.addAll(l);
			}
			return;
		}
		List<String> proList = notCopyProperties == null ? new ArrayList<>() : Arrays.asList(notCopyProperties);
		if(source instanceof Map) {//map复制
			Map sourceMap = (Map)source;
			if(target instanceof Map) {
				Map c = (Map)target;
				c.clear();
				c.putAll(sourceMap);
			} else {
				BeanMap targetMap = BeanMap.create(target);
				targetMap.forEach((key, value) -> {
					if (sourceMap.containsKey(key)) {
						Class<?> targetClass = targetMap.getPropertyType(key.toString());
						value = sourceMap.get(key);
						if ((nullCopy == null || DataUtils.isTrue(nullCopy))
								|| value != null)
							_copy(key, value, targetMap, targetClass, applyFun, proList);
					}
				});
			}
			return;
		}
		if(target instanceof Map) {//map复制
			Map targetMap = (Map)target;
			BeanMap sourceMap = BeanMap.create(source);
			targetMap.forEach((key, value) -> {
				if (sourceMap.containsKey(key)) {
					value = sourceMap.get(key);
					if ((nullCopy == null || DataUtils.isTrue(nullCopy))
							|| value != null)
						_copy(key, value, targetMap, Object.class, applyFun, proList);
				}
					
			});
			return;
		}
		//**采用CGLIB优化属性复制操作
		BeanMap sourceMap = BeanMap.create(source);
		BeanMap targetMap = BeanMap.create(target);
		targetMap.forEach((key, value) -> {
			if (sourceMap.containsKey(key)) {
				Class<?> targetClass = targetMap.getPropertyType(key.toString());
				value = sourceMap.get(key);
				if ((nullCopy == null || DataUtils.isTrue(nullCopy))
						|| value != null)
					_copy(key, value, targetMap, targetClass, applyFun, proList);
			}
			
		});
	}
	/**
	 * 复制属性
	 * @param key 原属性名
	 * @param value 原属性值
	 * @param targetMap 目标对象
	 * @param targetClass 目标对象存储类型
	 * @param applyFun 类型转换函数
	 * @param proList 不复制的属性名
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private static void _copy(Object key, Object value, Map targetMap, Class<?> targetClass, 
			BiFunction<Object, Class<?>, Object> applyFun, List<String> proList) {
		if (proList.contains(key) || !targetMap.containsKey(key) || key.equals("class"))
			return;
		if (value == null) {
			targetMap.put(key, null);
			return;
		}
		Class<?> sourceClass = value.getClass();
		if(applyFun != null) {
			value = applyFun.apply(value, targetClass);
		} else if(value != null && !targetClass.equals(sourceClass)) {//不同数据类型
			value = ClassUtils.forceCast(value, targetClass);
		}
		targetMap.put(key, value);
	}
	/**
	 * 将javaBean对象返回值不包含Set接口的属性存放值Map中
	 * 
	 * @param entity
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> describe(Object entity) {
		Map<String, Object> properties = new HashMap<String, Object>();
		if(entity == null)
			return properties;
		if (entity instanceof Map) {
			@SuppressWarnings("rawtypes")
			Map<Object, Object> em = (Map)entity;
			em.forEach((k, v) -> {
			   if (k != null || v != null) {
				   properties.put(k.toString(), v);
			   }
			 }
			);
			return properties;
		}
		BeanMap sourceMap = BeanMap.create(entity);
		sourceMap.forEach((key, value) -> {
			if(key.equals("class") || key.equals("classLoader") 
					|| value instanceof Class || value instanceof ClassLoader)//排除类属性和类加载器属性
				return;
			properties.put(key.toString(), value);
		});
		return properties;
	}
	/**
	 * 将javaBean对象返回值不包含Set接口的属性存放值Map中
	 * 
	 * @param entity
	 * @return
	 */
	public static Map<String, Class<?>> describeType(Class<?> beanClass) {
		Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
		if (beanClass.getName().indexOf("$") != -1){//代理类，还原成本类
			beanClass = beanClass.getSuperclass();
		}
		Method[] m = beanClass.getMethods();
		for (int i = 0; i < m.length; i++) {
			String mName = m[i].getName();
			if ((mName.startsWith("get") || mName.startsWith("is")) 
					&& !mName.equals("getClass")
					&& m[i].getParameterCount() == 0) {
				if (Modifier.isPublic(m[i].getModifiers())) {
					Class<?> rt = m[i].getReturnType();
					String prefex;
					if(mName.startsWith("get")){
						prefex = "get(\\w+)";
					}else{
						prefex = "is(\\w+)";
					}
					String key = mName.replaceFirst(prefex, "$1");
					key = StringUtils.firstLowerCase(key);
					properties.put(key, rt);
				}
			}
		}
		return properties;
	}
	/**
	 * 获取JavaBean某个属性值
	 * @param bean
	 * @param methodName
	 * @return
	 */
	public static Object getProperty(Object bean, String propertyName){
		if(propertyName.indexOf(".") != -1){
			String[] properties = propertyName.split(".");
			Object dobj = bean;
			for (int i = 0; i < properties.length; i++) {
				dobj = getProperty(dobj, properties[i]);
				if(dobj == null)
					return null;
			}
			return dobj;
		}
		FastMethod m = getterMethod(bean.getClass(), propertyName);
		if(m == null)
			return null;
		try {
			return m.invoke(bean, new Object[0]);
		} catch (InvocationTargetException e) {
			return null;
		}
	}
	/**
	 * 判断指定类beanClass是否拥有propertyName属性
	 * @param beanClass JavaBean
	 * @param propertyName get和set属性
	 * @return tru：拥有属性；false：不拥有
	 */
	public static boolean hasProperty(Class<?> beanClass, String propertyName){
		FastMethod m = getterMethod(beanClass, propertyName);
		return m != null;
	}
	
	/**
	 * 获取JavaBean某个属性的类型
	 * @param bean
	 * @param methodName
	 * @return 属性类型
	 */
	public static Class<?> getPropertyType(Class<?> beanClass, String propertyName){
		FastMethod m = getterMethod(beanClass, propertyName);
		return m == null ? null : m.getReturnType();
	}
	/**
	 * 取得指定属性的取数快速方法
	 * @param beanClass
	 * @param propertyName
	 * @return
	 */
	private static FastMethod getterMethod(Class<?> beanClass, String propertyName) {
		FastClass fc = FastClass.create(beanClass);
		String methodName = "get" + StringUtils.firstUpperCase(propertyName);
		try {
			FastMethod m = fc.getMethod(methodName, new Class[0]);
			if(m == null) {
				methodName = "is" + StringUtils.firstUpperCase(propertyName);
				m = fc.getMethod(methodName, new Class[0]);
			}
			return m;
		} catch (Exception e) {
			return null;
		} catch (Error e) {
			return null;
		}
	}
	/**
	 * 取得指定属性的取数快速方法
	 * @param beanClass
	 * @param propertyName
	 * @return
	 */
	private static FastMethod setterMethod(Class<?> beanClass, String propertyName, Class<?> propertyClass) {
		try {
			FastClass fc = FastClass.create(beanClass);
			String methodName = "set" + StringUtils.firstUpperCase(propertyName);
			FastMethod m = fc.getMethod(methodName, new Class[] {propertyClass});
			return m;
		} catch (Exception e) {
			return null;
		} catch (Error e) {
			return null;
		}
	}
	/**
	 * 设置JavaBean某个属性值
	 * @param bean JavaBean对象
	 * @param propertyName 属性名
	 * @param propertyValue 属性值
	 */
	public static void setProperty(Object bean, String propertyName, Object propertyValue){
		Class<?> propetyClass = getPropertyType(bean.getClass(), propertyName);
		setProperty(bean, propertyName, propertyValue, propetyClass);
	}
	/**
	 * 设置JavaBean某个属性值
	 * @param bean JavaBean对象
	 * @param propertyName 属性名
	 ** @param propetyClass 属性类型
	  @param propertyValue 属性值* 
	 */
	public static void setProperty(Object bean, String propertyName, Object propertyValue, Class<?> propetyClass){
		FastMethod m = setterMethod(bean.getClass(), propertyName, propetyClass);
		if(m == null)
			return;
		try {
			m.invoke(bean, new Object[] {propertyValue});
		} catch (InvocationTargetException e) {
		}
		
	}
	/**
	 * 获取javaBean对象返回值不包含Set接口的属性名
	 * 
	 * @param entity
	 * @return
	 */
	public static List<String> getBeanAttribute(Class<?> entityClass) {
		List<String> properties = new ArrayList<String>();
		try {
			String className = entityClass.getName();
			int ii;
			if ((ii = className.indexOf("$$")) != -1) {
				className = className.substring(0, ii);
				entityClass = Class.forName(className);
			}
			Method[] m = entityClass.getMethods();
			for (int i = 0; i < m.length; i++) {
				String mName = m[i].getName();
				if ((mName.startsWith("get") || mName.startsWith("is")) && !mName.equals("getClass")) {
					if (Modifier.isPublic(m[i].getModifiers())) {
						String c = String.valueOf(mName.charAt(3)).toLowerCase();
						String key = mName.replaceFirst("get(\\w)(\\w+)", c+ "$2");
						properties.add(key);
					}
				}
			}
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
		return properties;
	}
	/**
	 * 判断是否是代理实体类
	 * @param c
	 * @return
	 */
	public static boolean isProxy(Class<?> c) {
		if (c.getName().indexOf("$") != -1){//代理类，还原成本类
			return true;
		}else {
			return false;
		}
	}
	/**
	 * 判断两个对象相同的属性是否完全一致
	 * @param nativeObject
	 * @param targetObject
	 * @return
	 */
	public static boolean equals(Object nativeObject, Object targetObject, String... filterNames ) {
		Map<String, Object> nmap = describe(nativeObject);
		Map<String, Object> tmap = describe(targetObject);
		List<String> filters = Arrays.asList(filterNames);
		return !nmap.entrySet().stream()
			.filter(e -> !filters.contains(e.getKey()) && e.getValue() != null)//去除非空自动
			.anyMatch(e -> {
				Object p = tmap.get(e.getKey());
				if(p == null)//任意属性不存在
					return true;
				if(ClassUtils.isBaseClass(p.getClass())) {//String等基础类型
					return !p.equals(e.getValue());//任意属性不相等
				} else if(p.getClass().isEnum()){//枚举
					return !p.equals(e.getValue());//任意属性不相等
				} else if(p.getClass().isArray()){//数组
					return !Arrays.equals((Object[])p, (Object[])e.getValue());//任意属性不相等
				} else {//Bean对象
					return !p.equals(e.getValue());//任意属性不相等
				}
		});
	}
	
	/**
	 * 
	 * @param obj
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> objectToMap(Object obj) {
		Map<String, Object> map = new HashMap<String, Object>();
		if (obj == null) {
			return null;
		}
		Class<?> clazz = obj.getClass();
		List<Field> fieldList = new ArrayList<>();
		while (clazz != null) {
			fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
			clazz = clazz.getSuperclass();
		}
		try {
			Field[] fields = new Field[fieldList.size()];
			fieldList.toArray(fields);
			for (Field field : fieldList) {
				field.setAccessible(true);
				if (!Modifier.isStatic(field.getModifiers())) {
					map.put(field.getName(), field.get(obj));
				}
			}
		} catch (IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return map;
	}
}
